// Copyright (c) 1996-2002 John Lyon-Smith. All rights reserved.

#pragma once

#include <wxtl\Handle.h>

namespace wxtl
{
	class File : public Handle
	{
	public:
		File() throw()
		{
		}
		~File() throw()
		{
		}

		enum
		{
			// For open
			accessRead = 1,
			accessWrite = 2,
			accessReadWrite = (accessRead + accessWrite),
			shareNone = 0,
			shareRead = 4,		// Allow others to read the file
			shareWrite = 8,		// Allow others to write to the file
			shareReadWrite = (shareRead + shareWrite),

			// For seek
			seekBegin = FILE_BEGIN,
			seekEnd = FILE_END,
			seekCurrent = FILE_CURRENT,


			//
			optionSequential = FILE_FLAG_SEQUENTIAL_SCAN,

			// File attributes
			attrArchive = FILE_ATTRIBUTE_ARCHIVE,
			attrCompressed = FILE_ATTRIBUTE_COMPRESSED,
			attrDirectory = FILE_ATTRIBUTE_DIRECTORY,
			attrEncrypted = FILE_ATTRIBUTE_ENCRYPTED,
			attrHidden = FILE_ATTRIBUTE_HIDDEN,
			attrNormal = FILE_ATTRIBUTE_NORMAL,
			attrOffline = FILE_ATTRIBUTE_OFFLINE,
			attrReadOnly = FILE_ATTRIBUTE_READONLY,
			attrReparsePoint = FILE_ATTRIBUTE_REPARSE_POINT,
			attrSparseFile = FILE_ATTRIBUTE_SPARSE_FILE,
			attrSystem = FILE_ATTRIBUTE_SYSTEM,
			attrTemporary = FILE_ATTRIBUTE_TEMPORARY,
		};

		// Create a new file with specific attributes
		static void create(const TCHAR *fname, int dwAttrs = attrNormal) throw(...)
		{
			HANDLE handle = ::CreateFile(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL,
				CREATE_ALWAYS, dwAttrs, NULL);

			if (handle != INVALID_HANDLE_VALUE)
				CloseHandle(handle);
			else
				throw _win32_error(GetLastError());
		}

		static WIN32_FILE_ATTRIBUTE_DATA getFileAttributes(LPCTSTR filename)
		{
			WIN32_FILE_ATTRIBUTE_DATA data = {0};
			GetFileAttributesEx(filename, GetFileExInfoStandard, &data);
			return data;
		}
		
		static FILETIME getCreationTime(LPCTSTR filename)
		{
			WIN32_FILE_ATTRIBUTE_DATA data = getFileAttributes(filename);
			return data.ftCreationTime;
		}

		bool open(const char *fileName, unsigned flags = accessReadWrite | shareNone) throw(...)
		{
			DWORD dwDesiredAccess = 0;

			dwDesiredAccess |= ((flags & accessRead) ? GENERIC_READ : 0);
			dwDesiredAccess |= ((flags & accessWrite) ? GENERIC_WRITE : 0);

			DWORD dwShareMode = 0;

			dwShareMode |= ((flags & shareRead) ? FILE_SHARE_READ : 0);
			dwShareMode |= ((flags & shareWrite) ? FILE_SHARE_WRITE : 0);

			m_h = ::CreateFileA(fileName, dwDesiredAccess, 
				dwShareMode, NULL, OPEN_EXISTING, 0, NULL);

			if (m_h == INVALID_HANDLE_VALUE)
			{
				return false;
			}

			return true;
		}

		bool open(const wchar_t *fileName, unsigned flags = accessReadWrite | shareNone) throw(...)
		{
			DWORD dwDesiredAccess = 0;
			
			dwDesiredAccess |= ((flags & accessRead) ? GENERIC_READ : 0);
			dwDesiredAccess |= ((flags & accessWrite) ? GENERIC_WRITE : 0);

			DWORD dwShareMode = 0;

			dwShareMode |= ((flags & shareRead) ? FILE_SHARE_READ : 0);
			dwShareMode |= ((flags & shareWrite) ? FILE_SHARE_WRITE : 0);

			m_h = ::CreateFileW(fileName, dwDesiredAccess, 
				dwShareMode, NULL, OPEN_EXISTING, 0, NULL);

			if (m_h == INVALID_HANDLE_VALUE)
			{
				return false;
			}

			return true;
		}

		bool read(void *p, size_t desired, size_t* pActual = NULL)
		{ 
			DWORD actual;
			bool bOK = (ReadFile(m_h, p, (DWORD)(DWORD_PTR)desired, &actual, NULL) != FALSE);
			
			if (pActual)
				*pActual = (size_t)actual;

			return bOK;
		}

		bool write(const void *p, size_t desired, size_t *pActual = NULL)
		{ 
			DWORD actual;
			bool bOK = (WriteFile(m_h, p, (DWORD)(DWORD_PTR)desired, &actual, NULL) != FALSE);
			
			if (pActual)
				*pActual = (size_t)actual;
			
			return bOK;
		}

		bool writeString(LPCSTR string)
		{
			return write(string, strlen(string));
		}

		bool writeString(LPCWSTR string)
		{
			return write(string, wcslen(string) * sizeof(WCHAR));
		}

		

		LONGLONG size()
		{
			LARGE_INTEGER li;
			GetFileSizeEx(m_h, &li);
			return li.QuadPart;
		}
		
		bool setSize(LONGLONG size)
		{
			::SetFilePointerEx(m_h, *reinterpret_cast<LARGE_INTEGER*>(&size), NULL, seekBegin);
			return (::SetEndOfFile(m_h) == TRUE);
		}

		void seek(LONGLONG pos, unsigned base)
		{
			::SetFilePointerEx(m_h, *reinterpret_cast<LARGE_INTEGER*>(&pos), NULL, base);
		}
	};
}

